package inset;

import geom.Line;
import geom.Polygon;
import geom.Ray;

import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;


public class RawInsetGenerator
{
    public RawInsetGenerator(Polygon poly)
    {
        this.poly = poly;
        polyLines = poly.getLineSegments();
    }
    
    //Calculate the bisectors.  Associate each with 2 line endpoints
    public void generateBisectors()
    {         
        for (int i = 0, j = 1; i < polyLines.size(); i++, j = (j+1) % (polyLines.size()))
        {
            Line l1 = polyLines.get(i), l2 = polyLines.get(j);            
    
            calculateBisector(l1, l2);
            
            //Replace the old lines with the updated objects
            polyLines.remove(i);
            polyLines.add(i, l1);
            
            polyLines.remove(j);
            polyLines.add(j, l2);
        }
        
        int i = 0;
        for (Line l : polyLines)
        {                     
            l.fitToBisectors();
        }
    }
    
    public void calculateBisector(Line l1, Line l2)
    {
        Line cl1 = (Line) l1.clone();
        Line cl2 = (Line) l2.clone();

        // Determine which endpoints are the common ones
        if (cl1.getP1().equals(cl2.getP1()))
        {
            // Do nothing        	
        }        
        else if (cl1.getP2().equals(cl2.getP2()))
        {
        	cl1.swapEndpoints();
            cl2.swapEndpoints();            
        }
        else if (cl1.getP1().equals(cl2.getP2()))
        {            
            cl2.swapEndpoints();            
        }
        else if (cl1.getP2().equals(cl2.getP1()))
        {
            cl1.swapEndpoints();
        }
        else
        {
            System.err.println("No common endpoints");
        }

        Ray r1 = new Ray(cl1);
        Ray r2 = new Ray(cl2);

        Ray bisector = r1.determineBisector(r2);
        
        System.out.println(bisector.getDirection().x + " " + bisector.getDirection().y);
        
        bisector.normalize();
        bisector.setRayLength(0.1);
        
        if (bisector == null)
        {
        	System.out.println(l1);
        	System.out.println(l2);
        }
     
        switch (poly.getWindingDirection())
        {
        	case CW:
        	{
        		//Needs to be offset
        		if (poly.contains(bisector.getEndpoint()))
                    bisector.flip();
        		break;
        	}
        	case CCW:
        	{
        		//Needs to be inset
        		if (!poly.contains(bisector.getEndpoint()))
                    bisector.flip();
        	}
        }        
        
        //Since the calculation makes the origins equal, we know where to set the bisector
        l1.associateBisector(bisector, r1.getOrigin());
        l2.associateBisector(bisector, r2.getOrigin());        
    }
    
    public Polygon getInset(double dist)
    {
        offsetLines = new ArrayList<Line>();
        
        for (Line l : polyLines)
        {
            //Determine the direction the new line will move        	
            Ray r = new Ray(l);
            r.perpendicular();
            r.normalize();
            r.setRayLength(dist);            
            
            //I don't think flipping is required here - maybe because of the order of the lines?
            
            l = l.offset(r);            
            
            if (l != null)  
            {              
            	//TODO fix if bisectors wrong
                //l.fitToBisectors();
                
                //A null entry means the line was invalid
                if (l != null)                
                	offsetLines.add(l);
            }            
        }
        
        //TODO check if accurate
        //connectBrokenSegments(offsetLines);
        
        Polygon iPoly = new Polygon(offsetLines);        
        
        return iPoly;
    }
    
    private void connectBrokenSegments(ArrayList<Line> offsetLines)    
    {   
        for (int i = 0; i < offsetLines.size(); i++)
        {   
            int numRemoved = 0;
            while (i < offsetLines.size() && offsetLines.get(i) == null)
            {
                offsetLines.remove(i);
                numRemoved++;
                
                //If we have more null elements, wrap around
                if (offsetLines.size() != 0 && i == offsetLines.size() && offsetLines.get(0) == null)
                	i = 0;
            }
            
            //The entire shape was invalid (?)
            if (offsetLines.size() == 0)
            	return;
            
            //Unequal if there were any nulls
            if (numRemoved > 0)
            {                
                Line l1 = offsetLines.get(i % offsetLines.size());
                Line l2 = offsetLines.get((i-1 + offsetLines.size()) % offsetLines.size());
                
                //System.out.println("Using indicies: " + i + " " + ());
                
                //Process next point - not sure why they don't intersect
                if (l1 == null || l2 == null)
                	continue;
                	
                Point2D iPt = new Ray(l1).intersects(new Ray(l2));
                
                if (iPt != null)
                {
                    l1.extendTo(iPt);
                    l2.extendTo(iPt);
                }
            }
        }
    }
    
    public ArrayList<Ray> getBisectors()
    {
        ArrayList<Ray> bisectors = new ArrayList<Ray>();
        
        for (Line l : polyLines)
        {
        	System.out.println(l.p1Bisector);
        	bisectors.add(l.p1Bisector);
        }
        
        return bisectors;
    }

    private ArrayList<Line> polyLines, offsetLines;
    private Polygon poly;
}